package main

import (
	"fmt"
	"time"
)

/*
WithCancel 返回带有新 Done 通道的父节点的副本，当调用返回的 cancel 函数或当关闭父上下文的 Done 通道时，将关闭返回上下文的 Done 通道，无论先发生什么情况
*/
func main() {
	/*
		gen := func(ctx context.Context) <-chan int {
			dst := make(chan int)
			n := 1
			go func() {
				for {
					select {
					//Done 方法返回一个 Channel，这个 Channel 会在当前工作完成或者上下文被取消之后关闭，多次调用 Done 方法会返回同一个Channel；
					case <-ctx.Done():
						return // return结束该goroutine，防止泄露
					case dst <- n:
						n++
					}
				}
			}()
			return dst
		}
		ctx, cancel := context.WithCancel(context.Background())
		//gen 的调用者在使用生成的整数之后需要取消上下文，以免 gen 启动的内部 Goroutine 发生泄漏。
		defer cancel()
		for n := range gen(ctx) {
			fmt.Println(n)
			if n == 5 {
				break
			}
		}
	*/
	//错误示例，若不使用 context，则 goruntine 会发生泄露
	bugGen := func() <-chan int {
		ch := make(chan int)
		go func() {
			n := 1
			for {
				ch <- n
				n++
				time.Sleep(time.Second)
			}
		}()
		return ch
	}
	for n := range bugGen() {
		fmt.Println(n)
		if n == 5 {
			break
			//当n=5时，bugGen 函数的协程会执行无限循环，发生goruntine泄漏。
			//此处main函数执行完会自动结束，但是在web服务器中，http请求一般是一直在监听端口不会自动结束，会造成泄漏
		}
	}
}
